package com.msc.mobilecomputing.fragments;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.json.JSONObject;

import android.content.Context;
import android.graphics.Color;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.MapsInitializer;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;
import com.msc.mobilecomputing.R;
import com.msc.mobilecomputing.map.DirectionsJSONParser;
import com.msc.mobilecomputing.model.TravelLocation;

public class DirectionsFragment extends Fragment implements LocationListener {
	public static final String EXTRA = "location";

	private TravelLocation travelLocation;

	MapView mMapView;
	private GoogleMap googleMap;
	ArrayList<LatLng> mMarkerPoints;

	double mLatitude = 0;
	double mLongitude = 0;

	private LatLngBounds bounds;

	private boolean pathDrawn = false;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		travelLocation = (TravelLocation) getArguments().get(EXTRA);
		try {
			MapsInitializer.initialize(getActivity());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {

		View view = inflater.inflate(R.layout.fragment_directions, container,
				false);

		mMapView = (MapView) view.findViewById(R.id.mapView);
		mMapView.onCreate(savedInstanceState);
		mMapView.onResume();// needed to get the map to display immediately

		initUI(view);
		initMap();

		return view;
	}

	private void initUI(View view) {

		googleMap = mMapView.getMap();

		float dimension = getActivity().getResources().getDimension(
				R.dimen.map_padding);
		int padding = Math.round(dimension);

		MarkerOptions marker = new MarkerOptions().position(
				new LatLng(travelLocation.getLocation().getLon(),
						travelLocation.getLocation().getLat())).title(
				travelLocation.getName());

		googleMap.addMarker(marker);

	}

	private void setPath() {
		// if (!pathDrawn) {
		LatLng origin = new LatLng(mLatitude, mLongitude);
		com.msc.mobilecomputing.model.Location location = travelLocation
				.getLocation();
		LatLng dest = new LatLng(location.getLon(), location.getLat());

		LatLngBounds.Builder builder = new LatLngBounds.Builder();
		builder.include(origin);
		builder.include(dest);
		bounds = builder.build();

		// bounds = new LatLngBounds(origin, dest);

		String url = getDirectionsUrl(origin, dest);
		DownloadTask downloadTask = new DownloadTask();
		downloadTask.execute(url);
		// }
	}

	private void initMap() {
		// Initializing
		mMarkerPoints = new ArrayList<LatLng>();

		// Enable MyLocation Button in the Map
		googleMap.setMyLocationEnabled(true);

		// Getting LocationManager object from System Service LOCATION_SERVICE
		LocationManager locationManager = (LocationManager) getActivity()
				.getSystemService(Context.LOCATION_SERVICE);

		// Creating a criteria object to retrieve provider
		Criteria criteria = new Criteria();

		// Getting the name of the best provider
		String provider = locationManager.getBestProvider(criteria, true);

		// Getting Current Location From GPS
		Location location = locationManager
				.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);

		if (location != null) {
			onLocationChanged(location);
		}

		locationManager.requestLocationUpdates(provider, 20000, 0, this);

		// Setting onclick event listener for the map
		googleMap.setOnMapClickListener(new OnMapClickListener() {

			@Override
			public void onMapClick(LatLng point) {

				// // Already map contain destination location
				// if (mMarkerPoints.size() > 1) {
				//
				// mMarkerPoints.clear();
				// googleMap.clear();
				// LatLng startPoint = new LatLng(mLatitude, mLongitude);
				//
				// // draw the marker at the current position
				// drawMarker(startPoint);
				// }
				//
				// // draws the marker at the currently touched location
				// drawMarker(point);
				//
				// // Checks, whether start and end locations are captured
				// if (mMarkerPoints.size() >= 2) {
				// LatLng origin = mMarkerPoints.get(0);
				// LatLng dest = mMarkerPoints.get(1);
				//
				// // Getting URL to the Google Directions API
				// String url = getDirectionsUrl(origin, dest);
				//
				// DownloadTask downloadTask = new DownloadTask();
				//
				// // Start downloading json data from Google Directions API
				// downloadTask.execute(url);
				// }
			}
		});
	}

	private String getDirectionsUrl(LatLng origin, LatLng dest) {

		// Origin of route
		String str_origin = "origin=" + origin.latitude + ","
				+ origin.longitude;

		// Destination of route
		String str_dest = "destination=" + dest.latitude + "," + dest.longitude;

		// Sensor enabled
		String sensor = "sensor=false";

		// Building the parameters to the web service
		String parameters = str_origin + "&" + str_dest + "&" + sensor;

		// Output format
		String output = "json";

		// Building the url to the web service
		String url = "https://maps.googleapis.com/maps/api/directions/"
				+ output + "?" + parameters;

		return url;
	}

	/** A method to download json data from url */
	private String downloadUrl(String strUrl) throws IOException {
		String data = "";
		InputStream iStream = null;
		HttpURLConnection urlConnection = null;
		try {
			URL url = new URL(strUrl);

			// Creating an http connection to communicate with url
			urlConnection = (HttpURLConnection) url.openConnection();

			// Connecting to url
			urlConnection.connect();

			// Reading data from url
			iStream = urlConnection.getInputStream();

			BufferedReader br = new BufferedReader(new InputStreamReader(
					iStream));

			StringBuffer sb = new StringBuffer();

			String line = "";
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}

			data = sb.toString();

			br.close();

		} catch (Exception e) {
			Log.d("Exception while downloading url", e.toString());
		} finally {
			iStream.close();
			urlConnection.disconnect();
		}
		return data;
	}

	/** A class to download data from Google Directions URL */
	private class DownloadTask extends AsyncTask<String, Void, String> {

		// Downloading data in non-ui thread
		@Override
		protected String doInBackground(String... url) {

			// For storing data from web service
			String data = "";

			try {
				// Fetching the data from web service
				data = downloadUrl(url[0]);
			} catch (Exception e) {
				Log.d("Background Task", e.toString());
			}
			return data;
		}

		// Executes in UI thread, after the execution of
		// doInBackground()
		@Override
		protected void onPostExecute(String result) {
			super.onPostExecute(result);

			ParserTask parserTask = new ParserTask();

			// Invokes the thread for parsing the JSON data
			parserTask.execute(result);
		}
	}

	/** A class to parse the Google Directions in JSON format */
	private class ParserTask extends
			AsyncTask<String, Integer, List<List<HashMap<String, String>>>> {

		// Parsing the data in non-ui thread
		@Override
		protected List<List<HashMap<String, String>>> doInBackground(
				String... jsonData) {

			JSONObject jObject;
			List<List<HashMap<String, String>>> routes = null;

			try {
				jObject = new JSONObject(jsonData[0]);
				DirectionsJSONParser parser = new DirectionsJSONParser();

				// Starts parsing data
				routes = parser.parse(jObject);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return routes;
		}

		// Executes in UI thread, after the parsing process
		@Override
		protected void onPostExecute(List<List<HashMap<String, String>>> result) {
			ArrayList<LatLng> points = null;
			PolylineOptions lineOptions = null;

			// Traversing through all the routes
			for (int i = 0; i < result.size(); i++) {
				points = new ArrayList<LatLng>();
				lineOptions = new PolylineOptions();

				// Fetching i-th route
				List<HashMap<String, String>> path = result.get(i);

				// Fetching all the points in i-th route
				for (int j = 0; j < path.size(); j++) {
					HashMap<String, String> point = path.get(j);

					double lat = Double.parseDouble(point.get("lat"));
					double lng = Double.parseDouble(point.get("lng"));
					LatLng position = new LatLng(lat, lng);

					points.add(position);
				}

				// Adding all the points in the route to LineOptions
				lineOptions.addAll(points);
				lineOptions.width(10);
				lineOptions.color(Color.RED);

			}

			// Drawing polyline in the Google Map for the i-th route
			if (lineOptions != null && getActivity() != null) {
				Toast.makeText(getActivity(), "Direction found",
						Toast.LENGTH_SHORT).show();
				googleMap.addPolyline(lineOptions);
				pathDrawn = true;

				float dimension = getActivity().getResources().getDimension(
						R.dimen.map_padding);
				int padding = Math.round(dimension);

				googleMap.animateCamera(CameraUpdateFactory.newLatLngBounds(
						bounds, padding));

			} else {
				if (getActivity() != null) {
					Toast.makeText(getActivity(), "Unable to find Path",
							Toast.LENGTH_SHORT).show();
				}
			}
		}
	}

	private void drawMarker(LatLng point) {
		mMarkerPoints.add(point);

		// Creating MarkerOptions
		MarkerOptions options = new MarkerOptions();

		// Setting the position of the marker
		options.position(point);

		/**
		 * For the start location, the color of marker is GREEN and for the end
		 * location, the color of marker is RED.
		 */
		// if (mMarkerPoints.size() == 1) {
		// options.icon(BitmapDescriptorFactory
		// .defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
		// } else if (mMarkerPoints.size() == 2) {
		// options.icon(BitmapDescriptorFactory
		// .defaultMarker(BitmapDescriptorFactory.HUE_RED));
		// }

		// Add new marker to the Google Map Android API V2
		googleMap.addMarker(options);
	}

	@Override
	public void onLocationChanged(Location location) {
		// Draw the marker, if destination location is not set
		if (mMarkerPoints.size() < 2) {

			mLatitude = location.getLatitude();
			mLongitude = location.getLongitude();
			LatLng point = new LatLng(mLatitude, mLongitude);

			// googleMap.moveCamera(CameraUpdateFactory.newLatLng(point));
			// googleMap.animateCamera(CameraUpdateFactory.zoomTo(12));

			// drawMarker(point);

			setPath();
		}
	}

	@Override
	public void onProviderDisabled(String provider) {
		// TODO Auto-generated method stub
	}

	@Override
	public void onProviderEnabled(String provider) {
		// TODO Auto-generated method stub
	}

	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
		// TODO Auto-generated method stub
	}

}
